package com.amkai.aes.webservice.binary.objecthandler.diee;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.amkai.aes.aescommon.diee.GeneratedFiles;
import com.amkai.aes.aescommon.service.diee.AbstractDIEEJob;
import com.amkai.aes.aescommon.service.diee.AbstractDIEEResult;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;
import com.amkai.diee.entities.DIEEComplexEntity;
import com.amkai.diee.entities.EntityHolderBase;
import com.amkai.diee.utils.DIEEDebugger;
import com.amkai.diee.utils.Utils;
import com.amkai.rad.designerbase.beans.RADBean;

public abstract class AbstractDIEEHandler<E extends AbstractDIEEJob, OutputObject extends AbstractDIEEResult> extends AbstractLoggerHandler<E, Serializable, OutputObject> {
		
	// for testing purposes
	private boolean developmentMode = false;
	private List<Long[]> memoryLog;
	private long millis = 0;
	
	protected DIEEComplexEntity<RADBean> getEntity(Class<RADBean> clazz, SessionManager sessionManager) {
		DIEEComplexEntity<RADBean> entity = null;
		synchronized(EntityHolderBase.instance) {
			entity = EntityHolderBase.instance.getEntity(clazz);
		}
		if (entity==null) {
			info("The entity is NULL: "+clazz.getCanonicalName()+"!");
		} else {
			entity.setSessionManager(sessionManager);
		}
		return entity;
	}
	
	protected synchronized Thread getMemoryCheckThread() {
		if (memoryLog==null)
			memoryLog = new ArrayList<Long[]>();
		else
			memoryLog.clear();
		return new Thread() {
			@Override
			public void run() {
				while (true) {
					logMemory();
					try {
						sleep(1000);
					} catch (InterruptedException e) {
					}
				}
			}
		};
	}
	
	protected synchronized void clearMemoryLog() {
		if (memoryLog!=null) memoryLog.clear();
	}
	
	protected synchronized void logMemory() {
		if (developmentMode) System.gc();
		long secs = (System.currentTimeMillis() - millis) / 1000;
		Runtime r = Runtime.getRuntime();
		long used = r.totalMemory() - r.freeMemory();
		if (memoryLog==null) memoryLog = new ArrayList<Long[]>();
		memoryLog.add(new Long[] {secs, used});
		if (developmentMode) System.out.println("Used memory: "+used+" byte");
	}

	private synchronized final void writeMemoryCheckToFile(String fileName) throws IOException {
		if (memoryLog==null) return;
		File file = GeneratedFiles.getGeneratedFile(fileName);
		if (!file.exists()) file.createNewFile();
		StringBuilder str = new StringBuilder();
		for (Long[] l : memoryLog) {
			str.append(l[0]);
			str.append(", ");
			str.append(l[1]);
			str.append('\n');
		}
		Utils.saveStringToFile(str.toString(), file);
	}
	
	protected synchronized Long[] getTimeAndMemoryLimits() {
		Long[] result = new Long[4];
		if (memoryLog!=null) {
			for (Long[] values : memoryLog) {
				if (values!=null) {
					if (values[0]!=null) {
						if (result[0]==null || values[0]<result[0]) result[0] = values[0];
						if (result[1]==null || values[0]>result[1]) result[1] = values[0];
					}
					if (values[1]!=null) {
						if (result[2]==null || values[1]<result[2]) result[2] = values[1];
						if (result[3]==null || values[1]>result[3]) result[3] = values[1];
					}
				}
			}
		}
		return result;
	}
	
	protected void checkMemoryUsage(E job, Serializable object) throws Exception {
		Utils.checkMemoryUsage();
	}
	
	protected abstract String getName();
	
	protected abstract OutputObject createResult();
	
	protected abstract void process(E job, Serializable object, OutputObject result, SessionManager sessionManager, DIEEDebugger debugger) throws Exception, Error;
	
	@Override
	public OutputObject process(E job, Serializable object) throws Exception {
		line(getName()+" PROCESSING STARTED", true);
		millis = System.currentTimeMillis();
		DIEEDebugger debugger = new DIEEDebugger();
		developmentMode = job.isDevelopmentMode();
		info("Development mode: " + Boolean.toString(developmentMode)+".");
		SessionManager sessionManager = SiteManager.Start();
		OutputObject result = createResult();
		try {
			try {
				// AES-17128
				checkMemoryUsage(job, object);
			} catch (Exception ex) {
				result.setException(ex);
				return result;
			}
			sessionManager.openSession();
			process(job, object, result, sessionManager, debugger);
		} catch (Exception ex) {
			error(ex.getLocalizedMessage(), ex);
			result.setException(ex);
		} catch (Error err) {
			fatal(err.getLocalizedMessage(), err);
			result.setError(err);
		} finally {
			sessionManager.closeSession();
			if (developmentMode) writeMemoryCheckToFile("memory-log.csv");
		}
		debugger.log();
		line(getName()+" PROCESSING ENDED", false);
		return result;
	}

}
